﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Configuration.Provider;
using System.IO;
using System.Threading;
using FileMonitor.Configuration.Prototype;
using FileMonitor.Service.Tracking;
using FileMonitor.Service.Tracking.Manager;
using Microsoft.Practices.EnterpriseLibrary.Data.Sql;

namespace FileMonitor.Service.Prototype
{
    public enum ProcessState
    {
        Starting,
        Waitting,
        Running,
        Pause,
        Error
    }

    public abstract class FileMonitorBase :ProviderBase, TimerTrriger.ITriggerProcess, IDisposable
    {
        Queue<string> queueFiles ; 
        EventWaitHandle _wh = new AutoResetEvent(false);
        Thread _worker;
        private readonly object _locker = new object();
        public SqlDatabase exchangeDB;
      
        protected  FileProcessConfiguration baseConfiguration;

        internal RunBizProcess BizProcess { get; set; }
        public FileProcessConfiguration BaseConfiguration
        {
            get { return this.baseConfiguration; }
        }
       
        Utility.FileUtil util;
         
        private int moving_Mutex = 0;
        private object objMutex = new object();
        private ProcessState processState;
        public string MonitorPath
        {
            get
            {
                return this.baseConfiguration.MonitorPath;
            }
        }

        #region ProviderBase Members
        public override void Initialize(string name, NameValueCollection config)
        {
            base.Initialize(name, config);
            util = new Utility.FileUtil();
            if (config["MonitorPath"] == null)
            {
                throw new ArgumentException("Must_Set_MonitorPath");
            }
            exchangeDB = new SqlDatabase(ConfigurationManager.ConnectionStrings["DefaultExchangeDB"].ConnectionString);
            baseConfiguration = new  FileProcessConfiguration();
            baseConfiguration.Initialize(config);
            this._worker = new Thread(Work);
            this.queueFiles = new Queue<string>();
            this.BizProcess = new RunBizProcess(this);

        }
        #endregion

        public ProcessState ProcessState
        {
            get
            {
                return this.processState;
            }
        }

        public void Start()
        {
            this._worker.Start();
            this.BizProcess.Start();
            TimerTrriger.Trriger.RegisterTriggerProcess(this);
            processState = ProcessState.Running;
            TrackingService.CurrentTrackingManager.RegisterTrackingRouting(new ErrorLogTracking());
            TrackingService.CurrentTrackingManager.RegisterTrackingRouting(new MoveFileTracking());
            //TrackingService.CurrentTrackingManager.RegisterTrackingRouting(new ProcessMonitorTracking());
        }

        public void Stop()
        {
            StopWorkThread();
            this.BizProcess.Stop();
            TimerTrriger.Trriger.UnRegisterTriggerProcess(this);
            processState = ProcessState.Pause;
        }

        public void Enqueue(string fileFullPath)
        {
            lock (_locker)
            {
                queueFiles.Enqueue(fileFullPath);
            }
            _wh.Set();
        }

        private void StopWorkThread()
        {
            this.Enqueue(string.Empty);

        }

        private void Work()
        {
            while (true)
            {
                string fileFullPath = string.Empty;
                lock (_locker)
                {
                    if (this.queueFiles.Count > 0)
                    {
                        fileFullPath = this.queueFiles.Dequeue();
                        if (string.IsNullOrEmpty(fileFullPath))
                        {
                            return;
                        }
                    }
                }
                if (!string.IsNullOrEmpty(fileFullPath))
                {
                    FileSystemEventArgs args = util.InitializeFileEventArgs(fileFullPath, WatcherChangeTypes.Created);
                    Process(args);
                }
                else
                    _wh.WaitOne();
            }
        }

        #region Abstract Memebers
        public abstract void ProcessCreated(FileSystemEventArgs e);
        public abstract void ProcessChanged(FileSystemEventArgs e);
        public abstract void ProcessDeleted(FileSystemEventArgs e);
        #endregion

        #region ITriggerProcess Members
        public void TrrigerProcess()
        {
            MoveFile(this);
        }
        #endregion

        #region File Process Logic

        private void Process(FileSystemEventArgs e)
        {
            int waitTime = 1;
            Int32.TryParse(this.baseConfiguration.WaitTime, out waitTime);
            if (waitTime == 0)
            {
                waitTime = 1;
            }
            System.Threading.Thread.Sleep(waitTime * 1000);
            try
            {
                if (System.IO.File.Exists(e.FullPath))
                {
                    this.ProcessCreated(e);
                    TrackingTask task = new TrackingTask(this, TaskType.EndFile) { FilePath = e.FullPath };
                    TrackingService.CurrentTrackingManager.EnqueTask(task);
                }
            }
            catch (Exception ex)
            {
                TrackingTask task = new TrackingTask(this, TaskType.ErrorFile) { FilePath = e.FullPath, Exception = ex };
                TrackingService.CurrentTrackingManager.EnqueTask(task);
            }
        }

        private void MoveFile(object obj)
        {
            FileMonitorBase baseObj = obj as FileMonitorBase;
            try
            {
                if (Interlocked.Exchange(ref baseObj.moving_Mutex, 1) == 0)
                {
                    string path = baseObj.baseConfiguration.MonitorPath;
                    string[] files = System.IO.Directory.GetFiles(path);
                    if (files.Length > 0)
                    {
                        foreach (string fileName in files)
                        {
                            List<string> targetFiles = baseObj.util.MovePeocessFileReturnTarget(fileName, path, baseObj.baseConfiguration);
                            targetFiles.ForEach(f =>
                            {
                                TrackingTask task = new TrackingTask(this, TaskType.NewFile) { FilePath = f };
                                TrackingService.CurrentTrackingManager.EnqueTask(task);
                                this.Enqueue(f);
                            });
                        }
                    }
                    Interlocked.Exchange(ref baseObj.moving_Mutex, 0);
                }
            }
            catch (Exception ex)
            {
              // Log.getInstance().WriteToLog(ex.Message, baseConfiguration.LogPath);
            }
            finally
            {
                Interlocked.Exchange(ref baseObj.moving_Mutex, 0);
            }
        }

        #endregion


        private void GenerateNewBatch(ExcutedTask excutedTask)
        {
            TrackingTask task = new TrackingTask(this, TaskType.NewBatch) { BatchID=excutedTask.BatchId,FilePath=excutedTask.FileName };
            TrackingService.CurrentTrackingManager.EnqueTask(task);
        }

        private void EndBatch(ExcutedTask excutedTask)
        {
            TrackingTask task = new TrackingTask(this, TaskType.EndBatch) { BatchID = excutedTask.BatchId, FilePath = excutedTask.FileName };
            TrackingService.CurrentTrackingManager.EnqueTask(task);
        }

        protected void NotifyTask(ExcutedTask excutedTask)
        {
            if (excutedTask.IsLastFile)
            {
                this.BizProcess.NotifyRunBizProcess(excutedTask);
                EndBatch(excutedTask);
            }
            if (excutedTask.IsNewBacth)
            {
                GenerateNewBatch(excutedTask);
            }
        }

        #region IDisposable Members
        public void Dispose()
        {
            

        }
        #endregion

    }
}
